Skip to content

Fix PKCS#7 signature verification#195

Open
vcolin7 wants to merge 2 commits into
modelcontextprotocol:mainfrom
vcolin7:vcolin7/fix-verify-and-info
Open

Fix PKCS#7 signature verification#195
vcolin7 wants to merge 2 commits into
modelcontextprotocol:mainfrom
vcolin7:vcolin7/fix-verify-and-info

Conversation

@vcolin7
Copy link
Copy Markdown
Contributor

@vcolin7 vcolin7 commented Feb 21, 2026

Fixes: #21

This change fixes the verification of PKCS#7 signatures by using Node.js's built-in crypto module instead of node-forge by:

  1. Still parsing the PKCS#7 ASN.1 structure with node-forge
  2. Verifying the messageDigest authenticated attribute matches the content has calculated with Node.js's crypto.createHash()
  3. Verifying the RSA signature over the authenticated attributes using Node.js's crypto.createVerify()

Additionally, self-signed certificates now return "self-signed" status without requiring OS chain verification. This is not a functional change but a small optimization.

Motivation and Context

node-forge's pkcs7.verify() is not implemented and always throws with the following message: "PKCS#7 signature verification not yet implemented.". This caused mcpb verify and mcpb info to report all signed bundles as unsigned.

How Has This Been Tested?

The changes have been tested by running the existing test suite located at test/sign.e2e.test.ts, as well as by manually running the pack, sign, info, and verify CLI commands on the test servers under examples/hello-world-node in this repository.

Breaking Changes

No breaking changes are introduced; existing functionality remains intact.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)

  • New feature (non-breaking change which adds functionality)

  • Breaking change (fix or feature that would cause existing functionality to change)

  • Documentation update

Checklist

  • I have read the MCP Documentation

  • My code follows the repository's style guidelines

  • New and existing tests pass locally

  • I have added appropriate error handling

  • I have added or updated documentation as needed

Additional context

The implementation addresses the limitation of the node-forge library regarding PKCS#7 signature verification by using a custom function that adheres to RFC 5652 specifications.

andy-liner added a commit to andy-liner/mcpb that referenced this pull request Jun 2, 2026
`verifyMcpbFile()` called node-forge's `PkcsSignedData.verify()`, which is
not implemented and always throws "PKCS#7 signature verification not yet
implemented". The throw was caught and mapped to `status: "unsigned"`, so
`mcpb verify` and `mcpb info` reported *every* signed bundle as unsigned,
regardless of how it was signed. The `"self-signed"` status was also
unreachable: the OS trust-store check returned "unsigned" before it.

This implements the detached PKCS#7 verification manually:
- the signed `messageDigest` attribute must equal SHA-256 of the content, and
- the signer signature must validate over the DER-encoded authenticated
  attributes (re-tagged as a SET OF).

Content matching also accounts for the EOCD `comment_length` bump that
`signMcpbFile()` applies *after* signing (added in modelcontextprotocol#204): the stored content
can differ from the signed content by those two bytes. Verification accepts a
digest match against either the stored content or the comment_length-reversed
content, so it works for bundles from both current and older signers (and never
underflows the reversal when the comment_length was not bumped).

Trust levels: OS-trusted chain -> "signed"; self-signed (issuer CN == subject
CN) -> "self-signed"; valid signature but untrusted, non-self-signed chain ->
"unsigned".

The self-signed e2e test accepted both "self-signed" and "unsigned", so it
never caught this; it now requires "self-signed". Adds a regression test for a
signed bundle whose EOCD comment_length was not bumped.

Related: modelcontextprotocol#195 (championed verify fix, native crypto; lacks the EOCD handling
needed on current main), modelcontextprotocol#205, modelcontextprotocol#212. Fixes modelcontextprotocol#21.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ERROR: Extension is not signed

1 participant